#region imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Text;

using BO = PHSRAG.Insight21.BusinessObjects;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using SearchCriteria = PHSRAG.Insight21.SearchContext.SearchCriteria;
#endregion

namespace PHSRAG.Insight21.Personnel
{
	#region Class PersonnelSummaries
	/// <summary>
	/// The PersonnelSummaries object supports loading of summary information
	/// for all personnel to which the user has access. It also exposes methods to 
	/// support sorting and searching on the retrieved data.
	/// </summary>
	public class PersonnelSummaries:BO.BusinessObject
	{
		#region Constants
		private const string PersonnelSummariesKey			= "PersonnelSummarys_F1EB0573-FE58-4044-B6D5-0B2A72D0FD0F";
		private const string DefaultSortColumn				= "Name";
		private const string JobMapIdColumnName				= "EmployeeJobMapId";
		private const int DomainUserIdArgumentIndex			= 0;
		private const int SummaryDataTableIndex				= 0;
		private const int ProjectEndingFilterAll			= 0;
		private const int ProjectEndingFilterAlreadyExpired = -1;
		#endregion

		#region Instance Variables
		private Hashtable		personnelSummaryMap		= new Hashtable();
		private string			sortColumn				= DefaultSortColumn;
		private string			currentDataTableName	= Constants.OriginalDataTableName;
		private bool			sortAscending			= true;
		#endregion

		#region Constructor
		/// <summary>
		/// Intialize the base class with the prevalent cache policy
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public PersonnelSummaries() : base(null)
		{
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return PersonnelSummariesKey; } }
		/// <summary>
		/// Get the total number of personnel summary rows in the data container.
		/// Returns 0 if there is no data in the data container
		/// </summary>
		public int Count
		{
			get { return (DataContainer.Tables.Count == 0) ? 0 : DataContainer.Tables[currentDataTableName].Rows.Count; }
		}
		/// <summary>
		/// Get the sort sequence.
		/// </summary>
		public bool SortAscending
		{
			get { return sortAscending; }
		}

		/// <summary>
		/// Get the name of the sort column.
		/// </summary>
		public string SortColumn
		{
			get { return sortColumn; }
		}
		/// <summary>
		/// Get the table name of the active table in the data container
		/// This will be used for selective downloading of data into reports
		/// </summary>
		public string CurrentDataTableName
		{
			get { return currentDataTableName;}
		}
		#endregion

		#region BusinessObject Methods
		/// <summary>
		/// Load the personnel summary data for the current user and set the datatable name
		/// Sort the laoded data on the default sort column
		/// For the load the domain user id is mandatory.
		/// </summary>
		/// <param name="connectionString">Database connection string</param>
		/// <param name="args">Is expected to contain a single argument - the domain user ID</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (args.Length != 1)
				throw new Exception(@"Personnel Summaries load requires a domain user ID");

			DataContainer =	DataAccess.GetDataSet(
				connectionString
				,Constants.PersonnelSummariesSPROCName
				,DataAccess.BuildSqlParameterArray("@domainUserID",SqlDbType.Int,(int)args[DomainUserIdArgumentIndex]));

			if(DataContainer.Tables.Count == 1)
				DataContainer.Tables[SummaryDataTableIndex].TableName	= Constants.OriginalDataTableName;
			else
				throw new Exception(@"No personnel summary data found for the user");

			AddTableWithDistinctRows(DataContainer.Tables[Constants.OriginalDataTableName]);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Get the list of PersonnelSummary objects bound by a start and end index. 
		/// These indeces are into the list of personnel summaries that was loaded during the 
		/// Load() method execution into the data container. Typically, the start index is on 
		/// a "page" boundary, where the "page" referes to a datagrid page.
		/// A Hashtable of ArrayList objects is maintained using the start index as the key. 
		/// This allows retrieval of the set of PersonnelSummary objects associated with that 
		/// index, thereby directly supporting random access based the page index.
		/// </summary>
		/// <param name="startIndex">Index within the Personnel summary list to begin at</param>
		/// <param name="count">Total number of PersonnelSummary objects to load</param>
		/// <returns>The list of PersonnelSummary objects</returns>
		public ArrayList GetPersonnelSummaryList(int startIndex, int count)
		{
			ArrayList personnelSummaryList = personnelSummaryMap[startIndex] as ArrayList;
			if (personnelSummaryList == null)
			{
				personnelSummaryList = new ArrayList();
				DataTable summaryTable = DataContainer.Tables[currentDataTableName];
				for (int indexCount = startIndex, end = Math.Min(indexCount + count, summaryTable.Rows.Count); indexCount < end; ++indexCount)
					personnelSummaryList.Add(new PersonnelSummary(CachePolicy, summaryTable.Rows[indexCount]));
				personnelSummaryMap[startIndex] = personnelSummaryList;
			}
			return personnelSummaryList;
		}

		/// <summary>
		/// Search the DataContainer for rows which match the specified search criteria.
		/// Two DataTables are maintained within the DataContainer; a full-complement DataTable and a filtered one.
		/// This allows filtering without having to hit the database. If a filter is in
		/// effect, we simply set the value of the currentTableName to Constants.FilteredDataTableName so that the
		/// GetPersonnelSummaryList() method may retreive from the appropriate DataTable.
		/// </summary>
		/// <param name="searchCriteria">The search criteria instance picked from the search context</param>
		/// <remarks>True if the search criteria did any filtering; false otherwise</remarks>
		public bool Search(SearchCriteria searchCriteria)
		{
			personnelSummaryMap.Clear();

			StringBuilder filter = new StringBuilder();

			foreach (string key in searchCriteria.Keys)
			{
				switch (key.ToLower())
				{
                    case "employeefirstname":
                        filter.AppendFormat("{0} FirstName LIKE '{1}%'", (filter.Length > 0) ? "AND" : "", searchCriteria[key]);
                        break;
                    case "employeelastname":
                        filter.AppendFormat("{0} LastName LIKE '{1}%'", (filter.Length > 0) ? "AND" : "", searchCriteria[key]);
                        break;
                    //case "name":
                    //    filter.AppendFormat("{0} Name LIKE '{1}%'", (filter.Length > 0) ? "AND" : "", searchCriteria[key]);
                    //    break;
					case "employeenumber":
						filter.AppendFormat("{0} EmployeeID LIKE '{1}%'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "jobcode":
						filter.AppendFormat("{0} JobCode LIKE'%{1}%'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "status":
						filter.AppendFormat("{0} StatusID='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "hrdepartment":
						filter.AppendFormat("{0} HRDepartmentID='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "payfrequencytype":
						filter.AppendFormat("{0} PayFrequencyTypeID='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "effectivesalarydatefrom":
						filter.AppendFormat("{0} EffectiveDate>='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "effectivesalarydateto":
						filter.AppendFormat("{0} EffectiveDate<='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "viewbyprojectswithin":
						int projectEndingDate = Convert.ToInt32(searchCriteria[key]);
						string endDateFilter = string.Empty;
						if(projectEndingDate == ProjectEndingFilterAll)
							endDateFilter = string.Format("ProjectEndingDate<='{0}'"
								,Constants.DefaultProjectEndingDate);
						else if(projectEndingDate == ProjectEndingFilterAlreadyExpired)
							endDateFilter = string.Format("ProjectEndingDate<='{0}'"
								,DateTime.Now.AddDays(projectEndingDate).ToString("MM/dd/yyyy"));
						//30, 60 & 90 days
						else
							endDateFilter = string.Format("ProjectEndingDate>='{0}' AND ProjectEndingDate<='{1}'"
							,DateTime.Now.ToString("MM/dd/yyyy")
							,DateTime.Now.AddDays(projectEndingDate).ToString("MM/dd/yyyy"));
						filter.AppendFormat("{0} {1}",(filter.Length>0)?"AND":"",endDateFilter);
						break;
					case "pi":
						filter.AppendFormat("{0} PIID ='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "lab":
						filter.AppendFormat("{0} LabID ='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
				}
			}

			bool applySearch = (filter.Length > 0);
			DataTable summaryTable = DataContainer.Tables[Constants.OriginalDataTableName];
			if(applySearch)
			{
				DataTable filteredDataTable = summaryTable.Clone();
				filteredDataTable.TableName = currentDataTableName = Constants.FilteredDataTableName;

				DataRow[] filteredRows = summaryTable.Select(filter.ToString(),JobMapIdColumnName);
				foreach (DataRow summaryRow in filteredRows)
					filteredDataTable.ImportRow(summaryRow);

				AddTableWithDistinctRows(filteredDataTable);
			}
			else
				AddTableWithDistinctRows(summaryTable);

			return applySearch;
		}
		/// <summary>
		/// Sort the list of personnel summaries by the speicified column.
		/// If the column is to be sorted is the same as what was last sorted on, the sort sequence is toggled. Else, the
		/// sort sequence is set to ascending.
		/// </summary>
		/// <param name="columnName">Column to sort by</param>
		public void Sort(string columnName)
		{
			personnelSummaryMap.Clear();

			sortAscending = (sortColumn == columnName) ? !sortAscending : true;
			sortColumn = columnName;

			ProcessSort();
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Add a table with distinct rows to the data container
		/// </summary>
		/// <param name="summaryTable">datatable with personnel summary data,
		/// from which distinct rows will be picked</param>
		private void AddTableWithDistinctRows(DataTable summaryTable)
		{
			DataTable distinctTable		= summaryTable.Clone();
			distinctTable.TableName		= currentDataTableName = Constants.FilteredDataTableName;

			if(summaryTable.Rows.Count >= 1)
			{
				//Get the employeeJobMapID from the first row
				int employeeJobMapID = (int)summaryTable.Rows[0][JobMapIdColumnName];
			
				//Add the first row to the table
				distinctTable.ImportRow(summaryTable.Rows[0]);

				foreach (DataRow summaryRow in summaryTable.Rows)
				{
					//If the employeeJobMapID changes reset the value in the local variable
					//and import the row to the distinct data table
					if(employeeJobMapID != (int)summaryRow[JobMapIdColumnName])
					{
						employeeJobMapID = (int)summaryRow[JobMapIdColumnName];
						distinctTable.ImportRow(summaryRow);
					}
				}
			}						
			if(DataContainer.Tables.Contains(currentDataTableName))
				DataContainer.Tables.Remove(currentDataTableName);
			DataContainer.Tables.Add(distinctTable);

			ProcessSort();
		}
		/// <summary>
		/// Sorts the current data table on the sortColumn instance variable
		/// in the order specified by the sortOrder instance variable
		/// </summary>
		private void ProcessSort()
		{
			DataTable summaryTable = DataContainer.Tables[currentDataTableName];
			DataView summaryView = summaryTable.DefaultView;
			summaryView.Sort = string.Format("{0} {1}", 
				sortColumn, sortAscending ? Constants.SortOrderAscending : Constants.SortOrderDescending);

			DataTable sortedDataTable = summaryTable.Clone();
			foreach (DataRowView summaryRowView in summaryView)
				sortedDataTable.ImportRow(summaryRowView.Row);

			if(DataContainer.Tables.Contains(currentDataTableName))
				DataContainer.Tables.Remove(currentDataTableName);
			
			DataContainer.Tables.Add(sortedDataTable);
		}
		#endregion
	}
	#endregion
}